這一回先由畫面開始
在列表每一個 note 右邊加上修改按鈕
<.table id="notes" rows={@notes}>
<:col :let={note} label="ID"><%= note.id %></:col>
<:col :let={note} label="內容"><%= note.content %></:col>
<:action :let={note}>
<.link patch={~p"/#{note}/edit"}>修改</.link>
</:action>
</.table>
由於 ~p
會去檢查目前的 router 有沒有相對應的 path,這裡會有沒有這個路徑的警告。
在 router 加上修改路徑,這裡的 :id
對應到 ~p"/#{note}/edit"
的 #{note}
,寫 #{note.id}
也可以。
live "/:id/edit", NoteLive.Index, :edit
好了之後會發現其實已經可以按了,但是其實是新增。
所以要做的事情是修改 FormComponent
第一點的話我們可以在 NoteLive.Index
寫一個 :edit 專用的 handle_params 在裡面得到目前的 note
我們來改寫一下 handle_params 並使用我們自訂的 apply_action 來處理不同 action 時的行為
def handle_params(params, _uri, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
defp apply_action(socket, :edit, %{"id" => id}) do
{:ok, note} = Notes.get_note(id)
socket
|> assign(:page_title, "編輯感激筆記")
|> assign(:note, note)
end
defp apply_action(socket, :new, _params) do
socket
|> assign(:page_title, "新增感激筆記")
|> assign(:note, %Note{})
end
defp apply_action(socket, :index, _params) do
socket
|> assign(:page_title, "感激筆記列表")
|> assign(:note, nil)
end
接著把 note 傳入 FormComponent
<.live_component
module={GratitudeWeb.NoteLive.FormComponent}
id={@note.id || :new}
title={@page_title}
action={@live_action}
note={@note}
/>
(這邊除了 note 之外,也另外處理了不同 action 的標題)
傳入 note 之後 可以在 FormComponent update
的 params 拿到,修改 update
:
def update(%{note: note} = assigns, socket) do
form =
note
|> Note.changeset(%{})
|> to_form()
socket =
socket
|> assign(assigns) # 將所有的 assigns 傳入 LiveComponent 裡的 assigns
|> assign(form: form)
{:ok, socket}
end
因為我們有額外處理 title,所以要在 update
的時候也把 title 傳入,所以可以更新 component 的 header
<.header>
<%= @title %>
</.header>
最後則是要在 NoteLive.Index
的 handle_event
裡面處理加上修改的儲存
將 handle_event 改為依照 action 作不同的處理
def handle_event("save", %{"note" => note_params}, socket) do
save_note(socket, socket.assigns.action, note_params)
end
defp save_note(socket, :edit, note_params) do
case Notes.update_note(socket.assigns.note, note_params) do
{:ok, _note} ->
send(self(), :saved)
{:noreply,
socket
|> put_flash(:info, "修改成功")
|> push_patch(to: ~p"/")}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
end
end
defp save_note(socket, :new, note_params) do
case Notes.create_note(note_params) do
{:ok, _note} ->
send(self(), :saved)
{:noreply,
socket
|> put_flash(:info, "新增成功")
|> push_patch(to: ~p"/")}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
end
end
寫一個修改功能的測試,寫簡單的快樂流程即可
在 test/gratitude_web/live/note_live_test.exs
加入另一個 test 即可
test "updates note in listing", %{conn: conn} do
{:ok, note} = Notes.create_note(%{content: "感激筆記內容"})
{:ok, view, _html} = live(conn, ~p"/")
assert view |> element("a", "修改") |> render_click() =~ "編輯感激筆記"
assert_patch(view, ~p"/#{note}/edit")
assert view
|> form("#note-form", note: %{content: "修改後的感激筆記內容"})
|> render_submit()
assert_patch(view, ~p"/")
html = render(view)
assert html =~ "修改成功"
assert html =~ "修改後的感激筆記內容"
end